/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs     (C)ChaN, 2019        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/

#include "ff.h"			/* Obtains integer types */
#include "diskio.h"		/* Declarations of disk functions */
#include "qe_def.h"
#include "qe_list.h"
#include "qe_log.h"



#define FFDIO_LOG_DOMAIN		"ffdio"
#define ffdio_debug(...)     	qelog_debug(FFDIO_LOG_DOMAIN, __VA_ARGS__)
#define ffdio_info(...)			qelog_info(FFDIO_LOG_DOMAIN, __VA_ARGS__)
#define ffdio_notice(...)    	qelog_notice(FFDIO_LOG_DOMAIN, __VA_ARGS__)
#define ffdio_warning(...)		qelog_warning(FFDIO_LOG_DOMAIN, __VA_ARGS__)
#define ffdio_critical(...)		qelog_critical(FFDIO_LOG_DOMAIN, __VA_ARGS__)
#define ffdio_error(...)     	qelog_error(FFDIO_LOG_DOMAIN, __VA_ARGS__)
#define ffdio_fatal(...)    	qelog_fatal(FFDIO_LOG_DOMAIN, __VA_ARGS__)



/* Definitions of physical drive number for each drive */
#define DEV_RAM		0	/* Example: Map Ramdisk to physical drive 0 */
#define DEV_MMC		1	/* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB		2	/* Example: Map USB MSD to physical drive 2 */



QE_LIST_INIT(dev_list);

static ffdio_dev *get_ffdio_dev(BYTE pdrv)
{
	ffdio_dev *p;

	qe_list_foreach_entry(p, &dev_list, list) {
		if (p->pdrv_num == pdrv)
			return p;
	}

	return QE_NULL;
}


/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	qe_ret ret;
	
	ffdio_dev *dev = get_ffdio_dev(pdrv);
	if (!dev) {
		ffdio_error("dev %d not found", pdrv);
		return RES_PARERR;
	}

	ret = dev->ops->status(dev);
	if (ret != qe_ok) {
		ffdio_error("dev %d status error:%d", pdrv, ret);
		return RES_ERROR;
	}

	return 0;
}



/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
	qe_ret ret;

	ffdio_dev *dev = get_ffdio_dev(pdrv);
	if (!dev) {
		ffdio_error("dev %d not found", pdrv);
		return RES_PARERR;
	}

	ret = dev->ops->init(dev);
	if (ret != qe_ok) {
		ffdio_error("dev %d init error:%d", pdrv, ret);
		return RES_ERROR;
	}

	return 0;
}



/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
	BYTE *buff,		/* Data buffer to store read data */
	LBA_t sector,	/* Start sector in LBA */
	UINT count		/* Number of sectors to read */
)
{
	ffdio_dev *dev = get_ffdio_dev(pdrv);
	if (!dev) {
		ffdio_error("dev %d not found", pdrv);
		return -(RES_PARERR);
	}

	return dev->ops->read(dev, buff, sector, count);
}



/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0

DRESULT disk_write (
	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
	const BYTE *buff,	/* Data to be written */
	LBA_t sector,		/* Start sector in LBA */
	UINT count			/* Number of sectors to write */
)
{
	ffdio_dev *dev = get_ffdio_dev(pdrv);
	if (!dev) {
		ffdio_error("dev %d not found", pdrv);
		return -(RES_PARERR);
	}

	return dev->ops->write(dev, buff, sector, count);
}

#endif


/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	qe_ret ret;
	ffdio_dev *dev = get_ffdio_dev(pdrv);
	if (!dev) {
		ffdio_error("dev %d not found", pdrv);
		return -(RES_PARERR);
	}

	ret = dev->ops->ioctl(dev, cmd, buff);
	if (ret != qe_ok) {
		ffdio_error("dev %d ioctl cmd:%d error:%d", pdrv, cmd, ret);
		return -(RES_PARERR);
	}

	return 0;
}

qe_ret ffdio_dev_register(ffdio_dev *dev)
{
	ffdio_dev *p = get_ffdio_dev(dev->pdrv_num);
	if (p) {
		ffdio_error("dev %d already registed", dev->pdrv_num);
		return qe_err_exist;
	}

	qe_list_append(&dev->list, &dev_list);
	ffdio_debug("dev %d register success", dev->pdrv_num);

	return qe_ok;
}